<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>音频可视化播放器</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: 'Arial', sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            color: white;
        }

        .container {
            text-align: center;
            background: rgba(255, 255, 255, 0.1);
            backdrop-filter: blur(10px);
            border-radius: 20px;
            padding: 30px;
            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
            border: 1px solid rgba(255, 255, 255, 0.2);
        }

        h1 {
            margin-bottom: 30px;
            font-size: 2.5em;
            text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
        }

        .visualizer-container {
            margin: 20px 0;
            border-radius: 15px;
            overflow: hidden;
            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
        }

        #visualizer {
            display: block;
            background: linear-gradient(45deg, #1a1a2e, #16213e);
            border-radius: 15px;
        }

        .controls {
            margin: 20px 0;
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: 15px;
        }

        .file-input-wrapper {
            position: relative;
            overflow: hidden;
            display: inline-block;
        }

        .file-input-wrapper input[type=file] {
            position: absolute;
            left: -9999px;
        }

        .file-input-label {
            background: linear-gradient(45deg, #ff6b6b, #ee5a24);
            color: white;
            padding: 12px 25px;
            border-radius: 25px;
            cursor: pointer;
            transition: all 0.3s ease;
            font-weight: bold;
            box-shadow: 0 4px 15px rgba(255, 107, 107, 0.4);
        }

        .file-input-label:hover {
            transform: translateY(-2px);
            box-shadow: 0 6px 20px rgba(255, 107, 107, 0.6);
        }

        .audio-controls {
            display: flex;
            align-items: center;
            gap: 15px;
            background: rgba(255, 255, 255, 0.1);
            padding: 15px;
            border-radius: 15px;
            backdrop-filter: blur(5px);
        }

        .play-btn {
            background: linear-gradient(45deg, #4ecdc4, #44a08d);
            border: none;
            color: white;
            width: 50px;
            height: 50px;
            border-radius: 50%;
            cursor: pointer;
            font-size: 18px;
            transition: all 0.3s ease;
            box-shadow: 0 4px 15px rgba(78, 205, 196, 0.4);
        }

        .play-btn:hover {
            transform: scale(1.1);
            box-shadow: 0 6px 20px rgba(78, 205, 196, 0.6);
        }

        .volume-control {
            display: flex;
            align-items: center;
            gap: 10px;
        }

        .volume-slider {
            width: 100px;
            height: 5px;
            background: rgba(255, 255, 255, 0.3);
            border-radius: 5px;
            outline: none;
            -webkit-appearance: none;
        }

        .volume-slider::-webkit-slider-thumb {
            -webkit-appearance: none;
            width: 15px;
            height: 15px;
            background: #4ecdc4;
            border-radius: 50%;
            cursor: pointer;
        }

        .progress-bar {
            width: 200px;
            height: 5px;
            background: rgba(255, 255, 255, 0.3);
            border-radius: 5px;
            overflow: hidden;
        }

        .progress-fill {
            height: 100%;
            background: linear-gradient(45deg, #ff6b6b, #ee5a24);
            width: 0%;
            transition: width 0.1s ease;
        }

        .time-display {
            font-size: 12px;
            color: rgba(255, 255, 255, 0.8);
            min-width: 80px;
        }

        .info {
            margin-top: 20px;
            padding: 15px;
            background: rgba(255, 255, 255, 0.1);
            border-radius: 10px;
            font-size: 14px;
            color: rgba(255, 255, 255, 0.9);
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>🎵 音频可视化播放器</h1>
        
        <div class="visualizer-container">
            <canvas id="visualizer" width="600" height="300"></canvas>
        </div>

        <div class="controls">
            <div class="file-input-wrapper">
                <input type="file" id="audioFile" accept="audio/*">
                <label for="audioFile" class="file-input-label">
                    📁 选择音频文件
                </label>
            </div>

            <div class="audio-controls">
                <button class="play-btn" id="playBtn">▶️</button>
                
                <div class="progress-bar">
                    <div class="progress-fill" id="progressFill"></div>
                </div>
                
                <div class="time-display" id="timeDisplay">0:00 / 0:00</div>
                
                <div class="volume-control">
                    <span>🔊</span>
                    <input type="range" class="volume-slider" id="volumeSlider" min="0" max="100" value="50">
                </div>
            </div>
        </div>

        <div class="info">
            <p>支持格式：MP3, WAV, OGG, M4A 等常见音频格式</p>
            <p>上传音频文件后，点击播放按钮即可享受可视化效果</p>
        </div>
    </div>

    <audio id="audio" crossorigin="anonymous"></audio>

    <script>
        class AudioVisualizer {
            constructor() {
                this.canvas = document.getElementById('visualizer');
                this.ctx = this.canvas.getContext('2d');
                this.audio = document.getElementById('audio');
                this.playBtn = document.getElementById('playBtn');
                this.fileInput = document.getElementById('audioFile');
                this.volumeSlider = document.getElementById('volumeSlider');
                this.progressFill = document.getElementById('progressFill');
                this.timeDisplay = document.getElementById('timeDisplay');
                
                this.audioContext = null;
                this.analyser = null;
                this.source = null;
                this.dataArray = null;
                this.bufferLength = null;
                this.isPlaying = false;
                this.animationId = null;
                
                this.setupEventListeners();
                this.setupCanvas();
            }

            setupEventListeners() {
                this.fileInput.addEventListener('change', (e) => this.loadAudio(e));
                this.playBtn.addEventListener('click', () => this.togglePlay());
                this.volumeSlider.addEventListener('input', (e) => this.setVolume(e.target.value));
                this.audio.addEventListener('timeupdate', () => this.updateProgress());
                this.audio.addEventListener('ended', () => this.onAudioEnded());
            }

            setupCanvas() {
                const rect = this.canvas.getBoundingClientRect();
                this.canvas.width = rect.width * window.devicePixelRatio;
                this.canvas.height = rect.height * window.devicePixelRatio;
                this.ctx.scale(window.devicePixelRatio, window.devicePixelRatio);
                this.canvas.style.width = rect.width + 'px';
                this.canvas.style.height = rect.height + 'px';
            }

            async loadAudio(event) {
                const file = event.target.files[0];
                if (!file) return;

                const url = URL.createObjectURL(file);
                this.audio.src = url;
                
                try {
                    await this.setupAudioContext();
                    this.showNotification('音频文件加载成功！', 'success');
                } catch (error) {
                    this.showNotification('音频加载失败，请重试', 'error');
                    console.error('Audio loading error:', error);
                }
            }

            async setupAudioContext() {
                if (!this.audioContext) {
                    this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
                    this.analyser = this.audioContext.createAnalyser();
                    this.analyser.fftSize = 256;
                    this.bufferLength = this.analyser.frequencyBinCount;
                    this.dataArray = new Uint8Array(this.bufferLength);
                }

                if (this.source) {
                    this.source.disconnect();
                }

                this.source = this.audioContext.createMediaElementSource(this.audio);
                this.source.connect(this.analyser);
                this.analyser.connect(this.audioContext.destination);
            }

            async togglePlay() {
                if (this.audio.src === '') {
                    this.showNotification('请先选择音频文件', 'warning');
                    return;
                }

                if (this.isPlaying) {
                    this.audio.pause();
                    this.playBtn.textContent = '▶️';
                    this.isPlaying = false;
                    cancelAnimationFrame(this.animationId);
                } else {
                    try {
                        if (this.audioContext.state === 'suspended') {
                            await this.audioContext.resume();
                        }
                        await this.audio.play();
                        this.playBtn.textContent = '⏸️';
                        this.isPlaying = true;
                        this.visualize();
                    } catch (error) {
                        this.showNotification('播放失败，请重试', 'error');
                        console.error('Play error:', error);
                    }
                }
            }

            visualize() {
                this.animationId = requestAnimationFrame(() => this.visualize());
                
                this.analyser.getByteFrequencyData(this.dataArray);
                
                const canvasWidth = this.canvas.width / window.devicePixelRatio;
                const canvasHeight = this.canvas.height / window.devicePixelRatio;
                
                this.ctx.fillStyle = 'rgba(26, 26, 46, 0.2)';
                this.ctx.fillRect(0, 0, canvasWidth, canvasHeight);
                
                const barWidth = canvasWidth / this.bufferLength * 2;
                let x = 0;
                
                for (let i = 0; i < this.bufferLength; i++) {
                    const barHeight = (this.dataArray[i] / 255) * canvasHeight * 0.8;
                    
                    // 创建渐变色
                    const gradient = this.ctx.createLinearGradient(0, canvasHeight, 0, canvasHeight - barHeight);
                    gradient.addColorStop(0, `hsl(${i * 2 + Date.now() * 0.1}, 70%, 50%)`);
                    gradient.addColorStop(1, `hsl(${i * 2 + Date.now() * 0.1 + 60}, 70%, 70%)`);
                    
                    this.ctx.fillStyle = gradient;
                    this.ctx.fillRect(x, canvasHeight - barHeight, barWidth - 2, barHeight);
                    
                    // 添加发光效果
                    this.ctx.shadowColor = `hsl(${i * 2 + Date.now() * 0.1}, 70%, 50%)`;
                    this.ctx.shadowBlur = 10;
                    this.ctx.fillRect(x, canvasHeight - barHeight, barWidth - 2, barHeight);
                    this.ctx.shadowBlur = 0;
                    
                    x += barWidth;
                }
            }

            setVolume(value) {
                this.audio.volume = value / 100;
            }

            updateProgress() {
                const progress = (this.audio.currentTime / this.audio.duration) * 100;
                this.progressFill.style.width = progress + '%';
                
                const currentTime = this.formatTime(this.audio.currentTime);
                const duration = this.formatTime(this.audio.duration);
                this.timeDisplay.textContent = `${currentTime} / ${duration}`;
            }

            formatTime(seconds) {
                if (isNaN(seconds)) return '0:00';
                const mins = Math.floor(seconds / 60);
                const secs = Math.floor(seconds % 60);
                return `${mins}:${secs.toString().padStart(2, '0')}`;
            }

            onAudioEnded() {
                this.playBtn.textContent = '▶️';
                this.isPlaying = false;
                cancelAnimationFrame(this.animationId);
                this.progressFill.style.width = '0%';
            }

            showNotification(message, type) {
                // 创建通知元素
                const notification = document.createElement('div');
                notification.textContent = message;
                notification.style.cssText = `
                    position: fixed;
                    top: 20px;
                    right: 20px;
                    padding: 15px 20px;
                    border-radius: 10px;
                    color: white;
                    font-weight: bold;
                    z-index: 1000;
                    animation: slideIn 0.3s ease;
                    box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
                `;
                
                // 根据类型设置颜色
                const colors = {
                    success: 'linear-gradient(45deg, #4ecdc4, #44a08d)',
                    error: 'linear-gradient(45deg, #ff6b6b, #ee5a24)',
                    warning: 'linear-gradient(45deg, #feca57, #ff9ff3)'
                };
                
                notification.style.background = colors[type] || colors.success;
                
                // 添加动画样式
                const style = document.createElement('style');
                style.textContent = `
                    @keyframes slideIn {
                        from { transform: translateX(100%); opacity: 0; }
                        to { transform: translateX(0); opacity: 1; }
                    }
                `;
                document.head.appendChild(style);
                
                document.body.appendChild(notification);
                
                // 3秒后自动移除
                setTimeout(() => {
                    notification.style.animation = 'slideIn 0.3s ease reverse';
                    setTimeout(() => {
                        document.body.removeChild(notification);
                        document.head.removeChild(style);
                    }, 300);
                }, 3000);
            }
        }

        // 初始化可视化器
        document.addEventListener('DOMContentLoaded', () => {
            new AudioVisualizer();
        });
    </script>
</body>
</html>